/*
 * @Date: 2024-09-11 15:57:05
 * @Author: Yu Weifeng weifeng.yu@gientech.com
 * @LastEditTime: 2024-09-11 15:19:51
 * @Description: 监听元素显示
 * @Useage:
 *  template: <element v-displayed="handler" />
 *  JSX:
 *    const directives = [{ name: 'displayed', value: handler }];
 *    <div {...{ directives }}></div>
 */
import type { App, Directive, DirectiveBinding } from 'vue';
import { uniqueId } from 'lodash-es';

class EleObserver {
  // 元素回调函数映射表
  _eleHandlerMap;
  _observer;
  constructor() {
    this._observer = new IntersectionObserver(
      (elements) => {
        elements.forEach((element) => {
          let target = element.target;
          if (element.intersectionRatio > 0) {
            const callback = this._eleHandlerMap.get(target.getAttribute('displayed-id'));
            if (typeof callback == 'function') {
              callback();
            }
          }
        });
      },
      {
        // 0~1 相对元素自身的比例
        threshold: [0.1]
      }
    );
    this._eleHandlerMap = new Map();
  }

  add(element: Element, displayedId: string, handler: Function) {
    if (this._observer) {
      this._observer.observe(element);
      this._eleHandlerMap.set(displayedId, handler);
    }
  }

  remove(element: Element, displayedId: string) {
    this._observer.unobserve(element);
    this._eleHandlerMap.delete(displayedId);
  }
}

// 判断浏览器是否支持
let observer = null;
if (window.IntersectionObserver) {
  observer = new EleObserver();
}
const displayedDirective: Directive = {
  // 只调用一次，指令第一次绑定到元素时调用
  beforeMount(el: Element, binding: DirectiveBinding) {
    if (observer) {
      const displayedId = uniqueId('did_');
      el.setAttribute('displayed-id', displayedId);
      observer.add(el, displayedId, binding.value);
    }
  },
  // 只调用一次，指令与元素解绑时调用
  unmounted(el: Element, binding: DirectiveBinding) {
    if (observer) {
      observer.remove(el, el.getAttribute('displayed-id')!);
    }
  }
};

export function setupDisplayedDirective(app: App) {
  app.directive('displayed', displayedDirective);
}
